package cn.bigfire.wzdj.util.tool;

import cn.hutool.core.util.HexUtil;

import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteOrder;

public class WSFrame {


    public static final byte OPCODE_CONT = 0x0;
    public static final byte OPCODE_TEXT = 0x1;
    public static final byte OPCODE_BINARY = 0x2;
    public static final byte OPCODE_CLOSE = 0x8;
    public static final byte OPCODE_PING = 0x9;
    public static final byte OPCODE_PONG = 0xA;

    private byte one;
    private byte two;
    private byte[] realLenBytes;
    private int realLen = 0;
    private byte[] maskBytes;
    private byte[] payloadBytes;
    private boolean isRequest;


    public int getFinalFlag() {
        return (one & 0x80) >> 7;
    }
    public int getRsv() {
        return (one & 0x70) >> 4;
    }
    public int getOpCode() {
        return one & 0xF;
    }

    public String getType() {
        if (getOpCode() == OPCODE_CONT) {
            return "CONT";
        } else if (getOpCode() == OPCODE_TEXT) {
            return "TEXT";
        } else if (getOpCode() == OPCODE_BINARY) {
            return "BINARY";
        } else if (getOpCode() == OPCODE_CLOSE) {
            return "CLOSE";
        } else if (getOpCode() == OPCODE_PING) {
            return "PING";
        } else if (getOpCode() == OPCODE_PONG) {
            return "PONG";
        } else {
            return "UNKNOWN";
        }
    }

    public int getMaskFlag() {
        return (two & 0x80) >> 7;
    }
    public int getPayloadLen() {
        return (two & 0x7F);
    }


    //get
    public byte getOne() {
        return one;
    }

    public byte getTwo() {
        return two;
    }

    public byte[] getRealLenBytes() {
        return realLenBytes;
    }

    public int getRealLen() {
        return realLen;
    }

    public byte[] getMaskBytes() {
        return maskBytes;
    }

    public byte[] getPayloadBytes() {
        return payloadBytes;
    }

    public boolean isRequest() {
        return isRequest;
    }

    public void setRequest(boolean request) {
        isRequest = request;
    }

    //toString


    @Override
    public String toString() {
        return String.format("{fin=%s,opCode=%s,mask=%s,len=%s,realLen=%s,maskBytes=%s,payloadBytes=%s}",
                getFinalFlag(),
                getOpCode(),
                getMaskFlag(),
                getPayloadLen(),
                getRealLen(),
                HexUtil.encodeHexStr(getMaskBytes()),
                HexUtil.encodeHexStr(getPayloadBytes())
        );
    }

    /**
     * 读取一个ws帧
     *
     * @param inputStream inputStream
     * @return byte[] 字节数据集
     */
    public static WSFrame ofInputStream(InputStream inputStream,int id,boolean isRequest) throws IOException {
        //读取一个字节opCode
        int one = inputStream.read();
        boolean frameFinalFlag = (one & 0x80) != 0;
        int frameRsv = (one & 0x70) >> 4;
        int frameOpcode = one & 0x0F;
        if (frameOpcode == OPCODE_TEXT) {
            //文本类型
        } else if (frameOpcode == OPCODE_BINARY) {
            //字节类型
        } else {
//            log.error("不认识的OpCode:" + frameOpcode);
        }
        //读取一个字节长度
        int two = inputStream.read();
        boolean frameMasked = (two & 0x80) != 0;
        int framePayloadLen1 = two & 0x7F;
        int realLen = framePayloadLen1;//byte
        byte[] realLenBytes = new byte[0];
        if (framePayloadLen1 == 126) {//Short
            realLenBytes = new byte[2];
            inputStream.read(realLenBytes);
            realLen = cn.hutool.core.util.ByteUtil.bytesToShort(realLenBytes, ByteOrder.BIG_ENDIAN);
            if (realLen < 0) {
                realLen = Integer.parseInt(HexUtil.encodeHexStr(realLenBytes), 16);
            }
        } else if (framePayloadLen1 == 127) {//Long
            realLenBytes = new byte[8];
            inputStream.read(realLenBytes);
            realLen = (int) cn.hutool.core.util.ByteUtil.bytesToLong(realLenBytes, ByteOrder.BIG_ENDIAN);
            if (realLen < 0) {
                realLen = Integer.parseInt(HexUtil.encodeHexStr(realLenBytes), 16);
            }
        }
        byte[] markerBytes = new byte[4];
        if (frameMasked) {
            inputStream.read(markerBytes);
        }

        if (realLen < 0) {
            return null;
        }

        byte[] dataBytes = new byte[realLen];
        inputStream.read(dataBytes);
        //处理mask
        if (frameMasked) {
            dataBytes = XOR(dataBytes, markerBytes);
        }

        WSFrame wsFrame = new WSFrame();
        wsFrame.one = (byte) one;
        wsFrame.two = (byte) two;
        wsFrame.realLenBytes = realLenBytes;
        wsFrame.realLen = realLen;
        wsFrame.maskBytes = markerBytes;
        wsFrame.payloadBytes = dataBytes;
        return wsFrame;
    }


    /**
     * 可以理解为 或 运算 加密运行一次，解密再运行一次就行了
     *
     * @param data 原数据
     * @param mask mask websocket服务器随机生成的32位4字节的掩码
     * @return byte[]  掩码后
     */
    public static byte[] XOR(byte[] data, byte[] mask) {
        byte[] xorResult = new byte[Math.max(data.length, mask.length)];
        for (int i = 0; i < xorResult.length; i++) {
            if (i < data.length) {
                xorResult[i] = (byte) (data[i] ^ mask[i % 4]);
            }
        }
        return xorResult;
    }


}
